package com.deltapunkt.proxy;

import java.io.IOException;
import java.net.SocketAddress;
import java.nio.channels.SelectionKey;
import java.nio.channels.SocketChannel;
import java.util.concurrent.BlockingDeque;
import java.util.concurrent.LinkedBlockingDeque;

import com.deltapunkt.proxy.Command.Type;

public class PortForwardProxy implements Proxy {
	private SocketChannel client;
	private SocketChannel target;
	private BlockingDeque<Event> readQueue;
	private AsyncServer server;
	private State state;
	private SocketAddress targetAddress;
	
	public PortForwardProxy(AsyncServer server, SocketChannel client, SocketAddress targetAddress) {
		this.server = server;
		this.client = client;
		this.targetAddress = targetAddress;
		readQueue = new LinkedBlockingDeque<Event>(10);
		state = new State("init");
	}

	public void read(Event evt) {
		try {
			readQueue.putLast(evt);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}

	private void write(Message m) {
		try {
			server.addMessage(m);
			server.addCommand(new Command(Type.SETOPS, m.getChannel(),
					SelectionKey.OP_WRITE));
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}

	public synchronized void run() {
		if (state.is("init")) {
			state = new State("connecting");
			initTargetConnection();
		} else {
			Event e = readQueue.poll();
			if (e != null)
				if (e instanceof Message && state.is("connected")) {
					Message m = (Message) e;
					if (m != null) {
						if (m.getChannel() == client) {
							processFromClient(m);
						}
						if (m.getChannel() == target) {
							processFromTarget(m);
						}
					}
				} else if (e instanceof State) {
					state = (State) e;
					server.addCommand(new Command(Type.SETOPS, client,
							SelectionKey.OP_READ));
					server.addCommand(new Command(Type.SETOPS, target,
							SelectionKey.OP_READ));
					// if(state.is("connected")){
					// //send server command to register the client&target for
					// READ
					// }
				}
		}
	}

	private void initTargetConnection() {
		try {
			target = SocketChannel.open();
			target.configureBlocking(false);
			target.connect(targetAddress);
		} catch (IOException e1) {
			e1.printStackTrace();
		}
		// add register command
		server.mapSocket(target, this);
		Command cmd = new Command(Command.Type.REGISTER, target,
				SelectionKey.OP_CONNECT);
		server.addCommand(cmd);
		// target == new Connection(destHost, destPort);
		// initiate here connection
		System.out.println("Target connection initiated " + target);
	}

	private void processFromClient(Message m) {
		write(new Message(target, m.getContent()));
	}

	private void processFromTarget(Message m) {
		write(new Message(client, m.getContent()));
	}
}
